package com.tbit.main.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.tbit.common.utils.IpUtil;
import com.tbit.main.constant.DaoParamConstant;
import com.tbit.main.constant.RedisConstant;
import com.tbit.main.constant.UserConstant;
import com.tbit.main.dao.core.AccountUserDao;
import com.tbit.main.dao.core.GeoreGionBoundDao;
import com.tbit.main.exception.BaseException;
import com.tbit.main.pojo.*;
import com.tbit.main.service.*;
import com.tbit.main.util.DateTimeUtil;
import com.tbit.main.util.MD5PasswordUtil;
import com.tbit.main.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * 账号
 *
 * @author Leon
 * 2019年11月9日 下午2:09:42
 */
@Service("accountUserService")
@Slf4j
public class AccountUserServiceImpl implements AccountUserService {
    @Autowired
    private AccountUserDao accountUserDao;
    @Autowired
    private RedisService redisService;
    @Autowired
    private GeoreGionBoundDao georeGionBoundDao;
    @Resource
    private RoleService roleService;
    @Resource
    private RoleConfigService roleConfigService;
    @Resource
    private AccountService accountService;
    @Resource
    private TokenService tokenService;
    @Resource
    private LoginLogService loginLogService;
    @Resource
    private AccountUserFreeLoginService freeLoginService;

    @Override
    public AccountUser getByPhone(AccountUser accountUser) {
        return accountUserDao.getByPhone(accountUser);
    }

    @Override
    public void insert(AccountUser accountUser) {
        accountUser.setJoinTime(DateTimeUtil.getNowTime());
        accountUser.setUpdateTime(DateTimeUtil.getNowTime());
        accountUserDao.insert(accountUser);
    }

    @Override
    public void insertBound(AccountUserBound accountUserBound) {
        accountUserBound.setAddTime(DateTimeUtil.getNowTime());
        accountUserDao.insertBound(accountUserBound);
    }

    @Override
    @Transactional(value = "coreTransactionManager")
    public void update(AccountUser accountUser) {
        /**修改到期时间*/
        if (accountUser.getExpireDate() != null || accountUser.getSubRoleType() != null) {
            AccountUserBound accountUserBound = new AccountUserBound();
            accountUserBound.setAccountUserId(accountUser.getAccountUserId());
            accountUserBound.setSubRoleType(accountUser.getSubRoleType());
            accountUserBound.setExpireDate(accountUser.getExpireDate());
            accountUserDao.editExpire(accountUserBound);
        }
        List<Integer> regionId1 = accountUser.getRegionId();
        if (regionId1 != null && regionId1.size() > 0){
            georeGionBoundDao.deleteByUserId(accountUser.getAccountUserId());
            GeoreGionBound geoRegionBound = new GeoreGionBound();
            geoRegionBound.setIsLeader(0);
                for (Integer regionId : regionId1) {
                    geoRegionBound.setAccountId(accountUser.getAccountId());
                    geoRegionBound.setRegionId(regionId);
                    geoRegionBound.setAccountUserId(accountUser.getAccountUserId());
                    geoRegionBound.setBoundTime(DateTimeUtil.getNowTime());
                    geoRegionBound.setIsLeader(0);
                    georeGionBoundDao.binding(geoRegionBound);
                }
        }
        accountUser.setUpdateTime(DateTimeUtil.getNowTime());
        accountUserDao.update(accountUser);
    }

    @Override
    public void del(Integer accountuserId) {
        accountUserDao.del(accountuserId);
    }

    @Override
    public AccountUser getById(Integer accountUserId) {
        return accountUserDao.getById(accountUserId);
    }

    @Override
    public AccountUser getAccountUserById(Integer accountUserId) {
        return accountUserDao.getAccountUserById(accountUserId);
    }

    @Override
    public List<AccountUser> getByIds(List<Integer> accountUserIds) {
        return CollUtil.isEmpty(accountUserIds)
                ? Collections.emptyList()
                : accountUserDao.getByIds(StringUtil.getListString(accountUserIds));
    }

    @Override
    @Transactional(value = "coreTransactionManager")
    public void addBound(AccountUser accountUser) {
        insert(accountUser);
        AccountUserBound accountUserBound = new AccountUserBound();
        accountUserBound.setAccountUserId(accountUser.getAccountUserId());
        accountUserBound.setAccountId(accountUser.getAccountId());
        accountUserBound.setSubRoleType(accountUser.getSubRoleType());
        accountUserBound.setExpireDate(accountUser.getExpireDate());
        accountUserBound.setBrandId(accountUser.getBrandId());
        insertBound(accountUserBound);

    }

    @Override
    public List<AccountUser> getByAccountUserType(AccountUserBound accountUserBound) {
        return accountUserDao.getByAccountUserType(accountUserBound);
    }

    @Override
    public List<AccountUser> getRegionIdsUser(List<Integer> regionId, Integer subRoleType) {
        Map<String, Object> map = new HashMap<>();
        map.put("regionId", StringUtil.getListString(regionId));
        map.put("subRoleType", subRoleType);
        return accountUserDao.getRegionIdsUser(map);
    }

    @Override
    public List<AccountUser> getRegionIdsUserAndParam(List<Integer> regionId, Integer subRoleType,String param) {
        Map<String, Object> map = new HashMap<>();
        map.put("regionId", StringUtil.getListString(regionId));
        map.put("subRoleType", subRoleType);
        map.put("param", param);
        return accountUserDao.getRegionIdsUser(map);
    }

    @Override
    public AccountUserBound getBoundByUserId(Integer accountuserId) {
        return accountUserDao.getBoundByUserId(accountuserId);
    }

    @Override
    public List<HolderAccountBound> getHolderBoundByUserId(Integer accountuserId) {
        return accountUserDao.getHolderBoundByUserId(accountuserId);
    }

    @Override
    public List<Map<String, Object>> getByAccountIdAndType(List<Integer> accountIds, List<Integer> types, String expireDate) {
        Map<String, Object> map = new HashMap<>();
        map.put(DaoParamConstant.accountId, StringUtil.getListString(accountIds));
        map.put("type", StringUtil.getListString(types));
        map.put("expireDate", expireDate);
        return accountUserDao.getByAccountIdAndType(map);
    }

    @Override
    public List<AccountUser> getAll() {
        return accountUserDao.getAll();
    }

    @Override
    public List<AccountUser> getAllAccount() {
        return accountUserDao.getAllAccount();
    }

    @Override
    public void expireCheck(Integer accountUserId) {
        /**判断账号是否过期*/
        String key = RedisConstant.ACCOUNT_USER_EXPIRE_DATE + accountUserId;
        String expireDate = redisService.get(key);
        if (expireDate == null) {
            AccountUserBound accountUserBound = accountUserDao.getBoundByUserId(accountUserId);
            expireDate = accountUserBound == null ? "" : accountUserBound.getExpireDate();
            redisService.add(key, expireDate, 3600);
        }

        if (StrUtil.isNotBlank(expireDate)) {
            if (DateTimeUtil.isBeforeToday(expireDate)) {
                throw new BaseException("请先登录");
            }
        }
    }


    /**
     * 密码登录
     * @param brandId   品牌id
     * @param phone     手机号
     * @param password  加密后的密码
     * @param loginMethod   0：验证码，1：密码，2：密码+验证码 3：密码/验证码
     * @return  Result<Object>
     */
    @Override
    public Result<Object> loginWithPassword(Integer brandId, String phone, String password, Integer loginMethod, String ip, String userAgent) {
        /*防止暴力破解*/
        checkLogin(phone, ip);

        AccountUser accountUser = accountUserDao.getByPhone(new AccountUser("86", phone, brandId));
        if (Objects.isNull(accountUser)) {
            throw new BaseException("账号不存在");
        }

        if (StrUtil.isBlank(accountUser.getPassword())){
            throw new BaseException("用户未设置密码");
        }

        /*判断账号是否过期*/
        if (accountUser.getExpireDate() != null) {
            if (DateTimeUtil.isBeforeToday(accountUser.getExpireDate())) {
                throw new BaseException("账号已过期");
            }
        }

        if (!Objects.equals(MD5PasswordUtil.MD5("QTEDHAJCKIUDcnd." + password), accountUser.getPassword())) {
            preventBruteForceCracking(phone, ip);
            return Result.error("密码错误");
        }

        /*判断登录方式*/
        if (1 == loginMethod || 3 == loginMethod) {

            switch (accountUser.getSubRoleType()) {
                case 0:
                    accountUser.setAccountUserType(accountUser.getAccountType());
                    break;
                case 1:
                    accountUser.setAccountUserType(UserConstant.ACCOUNT_MAINTAIN_MANAGER);
                    break;
                case 2:
                    accountUser.setAccountUserType(UserConstant.ACCOUNT_MAINTAIN);
                    break;
                default:
                    accountUser.setAccountUserType(accountUser.getSubRoleType());
            }

            /*判断登录权限*/
            Integer accountUserType = accountUser.getAccountUserType();
            Role role = roleService.getRoleId(accountUserType);
            if (!Arrays.asList(1, 2).contains(role.getLogin())) {
                throw new BaseException("登录被限制");
            }
            accountUser.setRole(role);

            accountUser.setRoleConfig(roleConfigService.getRoleId(accountUserType));

            /*获取运营区域信息*/
            List<Account> accounts = new ArrayList<>();
            // 品牌
            if (accountService.getCountHolderAgent(accountUser.getAccountUserId()) > 0) {
                accounts = accountService.getHolderAgent(accountUser.getAccountUserId());
            } else if ((accountUserType.equals(UserConstant.ACCOUNT_BRAND) ||
                    accountUser.getRole().getRoleLevel().equals(UserConstant.ACCOUNT_BRAND))
                    && !accountUserType.equals(UserConstant.ACCOUNT_FRANCHISEE)
                    && !accountUserType.equals(UserConstant.ACCOUNT_ADOPT)) {
                /*加载品牌下的所有运营区域 */
                accounts = accountService.getBrandAgent(accountUser.getAccountId());
            }
            // 加盟商
            else if ((accountUserType.equals(UserConstant.ACCOUNT_AREA) ||
                    accountUser.getRole().getRoleLevel().equals(UserConstant.ACCOUNT_AREA))
                    && !accountUserType.equals(UserConstant.ACCOUNT_FRANCHISEE)) {
                /*加载加盟商下面的所有运营区域*/
                accounts = accountService.getByParentId(accountUser.getAccountId());
            }
            /*区域运营商、运维管理员、运维人员*/
            else {
                List<Integer> accountIds = new ArrayList<>();
                accountIds.add(accountUser.getAccountId());
                accounts = accountService.getByAccountIds(accountIds);
            }

            /*非领养人角色判断是有有运营区域*/
            if (!accountUserType.equals(UserConstant.ACCOUNT_ADOPT)) {
                if (CollUtil.isEmpty(accounts)) {
                    throw new BaseException("System.noAgent");
                }
                List<Agent> agents = new ArrayList<>();
                for (Account a : accounts) {
                    agents.add(new Agent(a.getAccountId(), a.getName(), a.getPhone()));
                }
                accountUser.setAgents(agents);

                /*设置区域运营商、运维管理员和运维人员归属区域id*/
                if (!accountUser.getAccountType().equals(UserConstant.ACCOUNT_BRAND) && !accountUser.getAccountType().equals(UserConstant.ACCOUNT_AREA)) {
                    accountUser.setAgentId(accountUser.getAgents().get(0).getAccountId());
                }
            }
            /* 生成token */
            String token = StringUtil.getUUID().replace("-", "");

            Account brand = accountService.getByAccountId(accountUser.getBrandId());
            Optional.ofNullable(brand).ifPresent(b -> accountUser.setBrandName(b.getName()));
            tokenService.setAccount(token, accountUser);
            accountUser.setToken(token);

            loginLogService.insert(new LoginLog(accountUserType, accountUser.getAccountUserId(), DateTimeUtil.getNowTime(), ip, StrUtil.isBlank(userAgent) ? "" : userAgent));

            /*设置是否需要提示设置密码*/
            accountUser.setHasPassword(StrUtil.isBlank(accountUser.getPassword()) ? 0 : 1);
            //刷新登录时间
            freeLoginService.refreshLoginDate(accountUser.getAccountUserId(), 7 * 24 * 3600, new Date());
            return Result.success(accountUser);
        }
        return Result.success(true);
    }


    /**
     * 修改密码
     * @param accountUser  当前登录用户
     * @param accountUserId 账户id
     * @param password  加密后密码
     * @return  Result<Object>
     */
    @Override
    public Result<Object> updatePassword(AccountUser accountUser, Integer accountUserId, String password) {
        log.info("{}修改了密码：", JSON.toJSONString(accountUser));
        accountUserDao.updatePassword(accountUserId, MD5PasswordUtil.MD5("QTEDHAJCKIUDcnd." + password));
        return Result.success();
    }

    /**
     * 校验登录
     * @param phone 手机号
     * @param ip    ip
     */
    private void checkLogin(String phone, String ip){
        String lockTime = redisService.get(RedisConstant.getLockTimeKey(phone, ip));
        if (StrUtil.isNotEmpty(lockTime)) {
            String msg = "操作频繁";
            try {
                msg = String.format("操作频繁，请%s后再试", DateUtil.formatBetween(Long.parseLong(lockTime) - System.currentTimeMillis(), BetweenFormatter.Level.SECOND));
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            throw new BaseException(msg);
        }
    }


    /**
     * 防止暴力破解
     * @param phone 手机号
     * @param clientIP  ip
     */
    private void preventBruteForceCracking(String phone, String clientIP) {
        try {
            String lockTimeKey = RedisConstant.getLockTimeKey(phone, clientIP);
            String key = RedisConstant.getLockCountKey(phone, clientIP);
            String value = redisService.get(key);
            if (StrUtil.isNotEmpty(value)) {
                //保存三十分钟
                redisService.add(key, "1", 30 * 60);
            } else {
                Integer count = Integer.parseInt(value);
                redisService.add(key, (++count).toString(), 30 * 60);
                if (count >= 3) {
                    //到达次数,添加锁定时间key，并且重置次数
                    log.info("{}登录频繁，ip地址为{}，已锁定10分钟", phone, clientIP);
                    redisService.add(lockTimeKey, String.valueOf(System.currentTimeMillis() + (60 * 10 * 1000)), 60 * 10);
                    redisService.add(key, "0", 30 * 60);
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

}
